#include "StdAfx.h"
#include "InvokeEndpoint.h"
#include "SimpleLogger.h"
#include "WindowsUtility.h"

namespace RoomFW {

InvokeEndpoint::InvokeEndpoint(PortIndex portIndex, IContractCheckerSPC protocol, const char* replyLog, const SimpleEvent& stopEvent) :
	portIndex_(portIndex),
	protocol_(protocol),
	replyLog_(replyLog),
	message_(),
	event_(),
	cs_(),
	stopEvent_(),
	isWaiting_(true)
{
	VERIFY(event_.create());
}

InvokeEndpoint::~InvokeEndpoint(void)
{
}

SendMessageError::Reason InvokeEndpoint::acceptMessage(
	MessageID messageID,
	IPayloadSP& payload,
	const char* senderName,
	IEndPointSP replyEndpoint)
{
	ASSERT(0 == replyEndpoint); // Required by interface, but not used for replies.

	// Contract violations are a programming error, hence the assertion.
	ASSERT(protocol_->validMessage(messageID, payload));
	SendMessageError::Reason retVal =
		(protocol_->validMessage(messageID, payload) ?
			SendMessageError::SMER_NONE :
			SendMessageError::SMER_CONTRACT_VIOLATION);
	if (SendMessageError::SMER_NONE == retVal)
	{
		// Accept the reply.
		CriticalSectionGuard g(cs_);
		if (isWaiting_)
		{
			// Log it.
#ifndef G_NO_LV_MESSAGE
			if (G_IS_LOGGING_MESSAGE && protocol_->getIsLogged())
			{
				std::stringstream logStr;
				logStr << '<' << senderName << '>';
				logStr << replyLog_;
				logStr << '<' << protocol_->messageName(messageID) << '>';
				if (0 == payload)
				{
					logStr << "<NULL>";
				}
				else
				{
					logStr << '<' << payload->toString() << '>';
				}
				G_LOG_0(LV_MESSAGE, logStr.str().c_str());
			}
#endif
			// Save the reply message and set the event.
			ASSERT(0 == message_); // Only one reply allowed.
			message_.reset(new RoomMessage(portIndex_, messageID, IPayload::ReleaseOwnership(payload)));
			event_.set();
		}
		else // Return an error.
		{
			retVal = SendMessageError::SMER_INVOKE_TIMEOUT;
		}
	}
	return retVal;
}

RoomMessageSP InvokeEndpoint::wait(DWORD timeout)
{
	// Wait with stop event.
	if (0 != stopEvent_)
	{
		const HANDLE waitObjs[] = {event_, stopEvent_};
		const size_t waitObjsSize = sizeof(waitObjs) / sizeof(HANDLE);
		DWORD ret = ::WaitForMultipleObjects(waitObjsSize, waitObjs, false, timeout);
		SHOW_LAST_ERROR((WAIT_FAILED != ret), "InvokeEndpoint::wait", "::WaitForMultipleObjects");
	}
	else // Wait without stop event.  Don't care about return.  We'll check for a message next.
	{
		event_.wait(timeout);
	}
	// Return the message, even if it's null.
	CriticalSectionGuard g(cs_);
	RoomMessageSP retVal = message_;
	message_.reset();
	isWaiting_ = false;
	return retVal;
}

}
